using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using Kent.Boogaart.HelperTrinity;

namespace Intuipic.Data
{
	/// <summary>
	/// The collection class that stores directories that contain images. Responsible for keeping the data normalized.
	/// </summary>
	/// <remarks>
	/// This class is not thread-safe.
	/// </remarks>
	[Serializable]
	public sealed class DirectoryCollection : ICollection<string>, INotifyCollectionChanged
	{
		private readonly List<string> _directories;

		public int Count
		{
			get
			{
				return _directories.Count;
			}
		}

		public bool IsReadOnly
		{
			get
			{
				return false;
			}
		}

		[field: NonSerialized]
		public event NotifyCollectionChangedEventHandler CollectionChanged;

		public DirectoryCollection()
		{
			_directories = new List<string>();
		}

		public void Add(string item)
		{
			ArgumentHelper.AssertNotNull(item, "item");

			string parentDirectory = Path.GetDirectoryName(item);
			bool allPresent = false;

			if (parentDirectory != null)
			{
				allPresent = true;
				string[] siblings = Directory.GetDirectories(parentDirectory);

				foreach (string sibling in siblings)
				{
					if (item.Equals(sibling, StringComparison.InvariantCultureIgnoreCase))
					{
						continue;
					}

					if (!Contains(sibling))
					{
						allPresent = false;
						break;
					}
				}
			}

			if (allPresent)
			{
				//all siblings are present, so just add the parent directory instead
				Add(parentDirectory);
			}
			else
			{
				List<string> redundantItems = new List<string>();

				foreach (string path in this)
				{
					if (item.StartsWith(path, StringComparison.InvariantCultureIgnoreCase))
					{
						//cannot duplicate directories nor directories inside directories that are already present in the collection
						return;
					}
					else if (path.StartsWith(item, StringComparison.InvariantCultureIgnoreCase))
					{
						//if we add the new item, then this item becomes redundant and should be removed
						redundantItems.Add(path);
					}
				}

				foreach (string redundantItem in redundantItems)
				{
					Remove(redundantItem);
				}

				//convert to lower invariant before storing
				item = item.ToLowerInvariant();
				_directories.Add(item);
				OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item));
			}
		}

		public void Clear()
		{
			_directories.Clear();
			OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
		}

		public bool Contains(string item)
		{
			ArgumentHelper.AssertNotNull(item, "item");
			return _directories.Contains(item.ToLowerInvariant());
		}

		public bool ContainsPathOrChildOf(string item)
		{
			ArgumentHelper.AssertNotNull(item, "item");

			foreach (string sourceDirectory in this)
			{
				if (sourceDirectory.StartsWith(item, StringComparison.InvariantCultureIgnoreCase))
				{
					return true;
				}
			}

			return false;
		}

		public bool ContainsPathOrParentOf(string item)
		{
			ArgumentHelper.AssertNotNull(item, "item");

			foreach (string sourceDirectory in this)
			{
				if (item.StartsWith(sourceDirectory, StringComparison.InvariantCultureIgnoreCase))
				{
					return true;
				}
			}

			return false;
		}

		public void CopyTo(string[] array, int arrayIndex)
		{
			_directories.CopyTo(array, arrayIndex);
		}

		public bool Remove(string item)
		{
			ArgumentHelper.AssertNotNull(item, "item");
			string parentDirectory = Path.GetDirectoryName(item);

			if ((parentDirectory != null) && (Contains(parentDirectory)))
			{
				//parent is present, so remove parent and add all other siblings
				Remove(parentDirectory);
				string[] siblings = Directory.GetDirectories(parentDirectory);

				foreach (string sibling in siblings)
				{
					if (item.Equals(sibling, StringComparison.InvariantCultureIgnoreCase))
					{
						continue;
					}

					Add(sibling);
				}

				return true;
			}
			else
			{
				bool itemFound = false;

				for (int i = 0; i < Count; ++i)
				{
					string currentItem = _directories[i];

					if (currentItem.StartsWith(item, StringComparison.InvariantCultureIgnoreCase))
					{
						_directories.RemoveAt(i);
						OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, currentItem, i));
						--i;
						itemFound = true;
					}
				}

				return itemFound;
			}
		}

		public IEnumerator<string> GetEnumerator()
		{
			return _directories.GetEnumerator();
		}

		System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
		{
			return _directories.GetEnumerator();
		}

		private void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
		{
			EventHelper.Raise(CollectionChanged, this, e);
		}
	}
}
